﻿'TODO: Document Gradient Panel
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Drawing.Drawing2D

<Designer("System.Windows.Forms.Design.ParentControlDesigner,System.Design", GetType(IDesigner)), _
ToolboxBitmap(GetType(GradientPanel), "GradientPanel.bmp")> _
Public Class GradientPanel
#Region "Defaults"
    Private Const def_Width As Integer = 200
    Private Const def_Height As Integer = 80
#End Region

#Region "Private Attributes"
    Private WithEvents _Border As BorderType
    Private WithEvents _Background As BackgroundType
#End Region

#Region "Event Handlers"
    Private Sub _Border_PropertyChanged(ByVal PropertyName As String) Handles _Border.PropertyChanged
        Me.Invalidate()
    End Sub
    Private Sub _Background_PropertyChanged(ByVal PropertyName As String) Handles _Background.PropertyChanged
        Me.Invalidate()
    End Sub
#End Region

#Region "Hidden Properties"
    <Browsable(False)> _
    Shadows Property BackColor() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
    <Browsable(False)> _
    Shadows Property BackgroundImage() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
    <Browsable(False)> _
    Shadows Property BackgroundImageLayout() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
    <Browsable(False)> _
    Shadows Property BorderStyle() As BorderStyle
        Get
        End Get
        Set(ByVal value As BorderStyle)
        End Set
    End Property
#End Region

#Region "Public Interface"
    Public Sub New()
        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.ResizeRedraw, True)
        Me.SetStyle(ControlStyles.UserPaint, True)

        Me.Size = New Size(def_Width, def_Height)
        _Border = New BorderType(Me)
        _Background = New BackgroundType()
    End Sub

    <Category("Appearance"), TypeConverter(GetType(BorderTypeConverter)), _
        Description("The panel border appearance.")> _
    Public Property Border() As BorderType
        Get
            Return _Border
        End Get
        Set(ByVal value As BorderType)
            _Border = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBorder()
        _Border.Reset()
    End Sub
    Public Function ShouldSerializeBorder() As Boolean
        Return _Border.ShouldSerialize()
    End Function

    <Category("Appearance"), TypeConverter(GetType(BackgroundTypeConverter)), _
      Description("The panel background appearance.")> _
    Public Property Background() As BackgroundType
        Get
            Return _Background
        End Get
        Set(ByVal value As BackgroundType)
            _Background = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBackground()
        _Background.Reset()
    End Sub
    Public Function ShouldSerializeBackground() As Boolean
        Return _Background.ShouldSerialize()
    End Function
#End Region

#Region "Private Methods"
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        UpdateLayout(e.Graphics)
    End Sub

    Private Sub UpdateLayout(ByRef myG As Graphics)
        myG.SmoothingMode = SmoothingMode.HighQuality

        'Create Border Polygon
        Dim R As Single = CSng(_Border.Radius)
        Dim BW As Single = CSng(_Border.Width)

        Dim ClientRect As Rectangle = Me.ClientRectangle
        If ClientRect.Height < (R + BW) * 2 Then
            R = ClientRect.Height \ 2 - BW
        End If
        If ClientRect.Width < (R + BW) * 2 Then
            R = ClientRect.Width \ 2 - BW
        End If

        Dim S As Single = R * 2
        Dim gp As New GraphicsPath

        Dim AntiAliasAdjust As Single = 2

        Dim X As Single = ClientRect.X + BW / 2
        Dim Y As Single = ClientRect.Y + BW / 2
        Dim W As Single = ClientRect.Width - AntiAliasAdjust - BW
        Dim H As Single = ClientRect.Height - AntiAliasAdjust - BW

        If _Border.Radius = 0 Then
            'No Rounding
            gp.AddLine(X, Y, X + W, Y)
            gp.AddLine(X + W, Y, X + W, Y + H)
            gp.AddLine(X + W, Y + H, X, Y + H)
            gp.AddLine(X, Y + H, X, Y)
            gp.CloseFigure()
        Else
            'Apply Rounding
            gp.AddArc(X, Y, S, S, 180, 90)
            If S < W Then gp.AddLine(X + R, Y, X + W - R, Y)
            gp.AddArc((X + (W - S)), Y, S, S, 270, 90)
            If S < H Then gp.AddLine(X + W, Y + R, X + W, Y + H - R)
            gp.AddArc((X + (W - S)), (Y + (H - S)), S, S, 360, 90)
            If S < W Then gp.AddLine(X + W - R, Y + H, X + R, Y + H)
            gp.AddArc(X, (Y + (H - S)), S, S, 90, 90)
            If S < H Then gp.AddLine(X, Y + H - R, X, Y + R)
            gp.CloseFigure()
        End If


        'Fill Gauge Polygon
        If _Background.FillStyle = BackgroundType.FillStyles.Gradient Then
            Dim myBrush As LinearGradientBrush
            myBrush = New LinearGradientBrush(New Rectangle(CInt(X), CInt(Y), _
                                                               CInt(W + AntiAliasAdjust), CInt(H + AntiAliasAdjust)), _
                                                               Me._Background.BackColor1, Me._Background.BackColor2, Me._Background.FillGradientStyle)
            myG.FillPath(myBrush, gp)
        Else
            myG.FillPath(New SolidBrush(Me._Background.BackColor1), gp)
        End If

        'Draw Border
        If _Border.Width > 0 Then
            Dim myPen As New Pen(_Border.Color, _Border.Width)
            myPen.Alignment = PenAlignment.Center
            myG.DrawPath(myPen, gp)
        End If
    End Sub
#End Region

#Region "Border Type"
    Public Class BorderType
        Private def_BorderWidth As Integer = 1
        Private def_BorderRadius As Integer = 5
        Private def_BorderColor As Color = Color.Black

        Private _BorderWidth As Integer
        Private _BorderRadius As Integer
        Private _BorderColor As Color

        Private _ParentControl As Control = Nothing

        Public Event PropertyChanged(ByVal PropertyName As String)


        Friend Sub New(ByRef Control As Control)
            _ParentControl = Control

            _BorderWidth = def_BorderWidth
            _BorderRadius = def_BorderRadius
            _BorderColor = def_BorderColor
        End Sub

        <Description("The border thickness. Use a zero value to hide border.")> _
        Public Property Width() As Integer
            Get
                Return _BorderWidth
            End Get
            Set(ByVal value As Integer)
                _BorderWidth = value
                RaiseEvent PropertyChanged("Width")
            End Set
        End Property
        Public Sub ResetWidth()
            Width = def_BorderWidth
        End Sub
        Public Function ShouldSerializeWidth() As Boolean
            Return Not (Width.Equals(def_BorderWidth))
        End Function

        <Description("The radius of the rounded corners. Use a zero value for square corners.")> _
        Public Property Radius() As Integer
            Get
                Return _BorderRadius
            End Get
            Set(ByVal value As Integer)
                _BorderRadius = value
                If _BorderRadius < 0 Then _BorderRadius = 0
                Dim ClientRect As Rectangle = _ParentControl.ClientRectangle
                If ClientRect.Height < (_BorderRadius + _BorderWidth) * 2 Then
                    _BorderRadius = ClientRect.Height \ 2 - _BorderWidth
                End If
                If ClientRect.Width < (_BorderRadius + _BorderWidth) * 2 Then
                    _BorderRadius = ClientRect.Width \ 2 - _BorderWidth
                End If
                RaiseEvent PropertyChanged("Radius")
            End Set
        End Property
        Public Sub ResetRadius()
            Radius = def_BorderRadius
        End Sub
        Public Function ShouldSerializeRadius() As Boolean
            Return Not (Radius.Equals(def_BorderRadius))
        End Function

        <Description("The border color.")> _
        Public Property Color() As Color
            Get
                Return _BorderColor
            End Get
            Set(ByVal value As Color)
                _BorderColor = value
                RaiseEvent PropertyChanged("Color")
            End Set
        End Property
        Public Sub ResetColor()
            Color = def_BorderColor
        End Sub
        Public Function ShouldSerializeColor() As Boolean
            Return Not (Color.Equals(def_BorderColor))
        End Function

        Public Sub Reset()
            _BorderWidth = def_BorderWidth
            _BorderRadius = def_BorderRadius
            _BorderColor = def_BorderColor

            RaiseEvent PropertyChanged("All")
        End Sub
        Public Function ShouldSerialize() As Boolean
            Return ShouldSerializeWidth() OrElse ShouldSerializeRadius() OrElse ShouldSerializeColor()
        End Function

        Public Overrides Function ToString() As String
            Return "(Border)"
        End Function
    End Class
    Public Class BorderTypeConverter
        Inherits TypeConverter

        Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function
        Public Overrides Function GetProperties(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object, ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
            Return TypeDescriptor.GetProperties(GetType(BorderType))
        End Function
    End Class
#End Region

#Region "Background Type"
    Public Class BackgroundType
        'Defaults
        Private def_BackColor1 As Color = Color.Red
        Private def_BackColor2 As Color = Color.Gold
        Private def_FillStyle As FillStyles = FillStyles.Gradient
        Private def_FillGradientStyle As LinearGradientMode = LinearGradientMode.Horizontal

        'Private Attributes
        Private _BackColor1 As Color = def_BackColor1
        Private _BackColor2 As Color = def_BackColor2
        Private _FillStyle As FillStyles = def_FillStyle
        Private _FillGradientStyle As LinearGradientMode = def_FillGradientStyle

        'Enumerations
        Public Enum FillStyles As Integer
            Solid = 0
            Gradient = 1
        End Enum

        'Public Events
        Public Event PropertyChanged(ByVal PropertyName As String)

        Friend Sub New()
        End Sub

        Public Property BackColor1() As Color
            Get
                Return _BackColor1
            End Get
            Set(ByVal value As Color)
                _BackColor1 = value
                RaiseEvent PropertyChanged("BackColor1")
            End Set
        End Property
        Public Sub ResetBackColor1()
            BackColor1 = def_BackColor1
        End Sub
        Public Function ShouldSerializeBackColor1() As Boolean
            Return Not (BackColor1.Equals(def_BackColor1))
        End Function

        Public Property BackColor2() As Color
            Get
                Return _BackColor2
            End Get
            Set(ByVal value As Color)
                _BackColor2 = value
                RaiseEvent PropertyChanged("BackColor2")
            End Set
        End Property
        Public Sub ResetBackColor2()
            BackColor2 = def_BackColor2
        End Sub
        Public Function ShouldSerializeBackColor2() As Boolean
            Return Not (BackColor2.Equals(def_BackColor2))
        End Function

        Public Property FillStyle() As FillStyles
            Get
                Return _FillStyle
            End Get
            Set(ByVal value As FillStyles)
                _FillStyle = value
                RaiseEvent PropertyChanged("FillStyle")
            End Set
        End Property
        Public Sub ResetFillStyle()
            FillStyle = def_FillStyle
        End Sub
        Public Function ShouldSerializeFillStyle() As Boolean
            Return Not (FillStyle.Equals(def_FillStyle))
        End Function

        Public Property FillGradientStyle() As LinearGradientMode
            Get
                Return _FillGradientStyle
            End Get
            Set(ByVal value As LinearGradientMode)
                _FillGradientStyle = value
                RaiseEvent PropertyChanged("FillGradientStyle")
            End Set
        End Property
        Public Sub ResetFillGradientStyle()
            FillGradientStyle = def_FillGradientStyle
        End Sub
        Public Function ShouldSerializeFillGradientStyle() As Boolean
            Return Not (FillGradientStyle.Equals(def_FillGradientStyle))
        End Function

        Public Sub Reset()
            _BackColor1 = def_BackColor1
            _BackColor2 = def_BackColor2
            _FillStyle = def_FillStyle
            _FillGradientStyle = def_FillGradientStyle

            RaiseEvent PropertyChanged("All")
        End Sub
        Public Function ShouldSerialize() As Boolean
            Return (ShouldSerializeBackColor1() OrElse ShouldSerializeBackColor2() OrElse ShouldSerializeFillStyle() OrElse ShouldSerializeFillGradientStyle())
        End Function

        Public Overrides Function ToString() As String
            Return "(Background)"
        End Function
    End Class
    Public Class BackgroundTypeConverter
        Inherits TypeConverter

        Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function
        Public Overrides Function GetProperties(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object, ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
            Return TypeDescriptor.GetProperties(GetType(BackgroundType))
        End Function
    End Class
#End Region


End Class
